#ifndef STATE_MACHINE_H
#define STATE_MACHINE_H

#include <cassert>
#include <string>

#include "State.h"


template <class entity_type>
class StateMachine
{
private:
	entity_type* m_owner;

	State<entity_type>* m_currState;
	State<entity_type>* m_prevState;

	State<entity_type>* m_currGlobalState;
	State<entity_type>* m_prevGlobalState;

public:
	StateMachine(entity_type* owner)
		: m_owner(owner)
		, m_currState(NULL)
		, m_prevState(NULL)
		, m_currGlobalState(NULL)
		, m_prevGlobalState(NULL)
	{}
	
	virtual ~StateMachine(){}
	
	void Update() const
	{
		if (m_currGlobalState)
		{
			m_currGlobalState->Execute(m_owner);
		}

		if (m_currState)
		{
			m_currState->Execute(m_owner);
		}
	}

	bool HandleMessage(const Message& msg)
	{
		if (m_currGlobalState && m_currGlobalState->OnMessage(m_owner, msg))
		{
			return true;
		}

		if (m_currState && m_currState->OnMessage(m_owner, msg))
		{
			return true;
		}

		return false;
	}
	
	void ChangeCurrentState(State<entity_type>* state)
	{
		assert(state && "<StateMachine::ChangeCurrentState> Trying to assign a null state");

		if (state)
		{
			if (m_currState == NULL)
			{
				m_currState = state;

				m_currState->Enter(m_owner);
			}
			else
			{
				m_prevState = m_currState;

				m_currState->Exit(m_owner);

				m_currState = state;

				m_currState->Enter(m_owner);
			}
		}
	}

	void ChangeCurrentGlobalState(State<entity_type>* state)
	{
		assert(state && "<StateMachine::ChangeCurrentGlobalState> Trying to assign a null state");

		if (state)
		{
			if (m_prevGlobalState == NULL)
			{
				m_currGlobalState = state;

				m_currGlobalState->Enter(m_owner);
			}
			else
			{
				m_prevGlobalState = m_currGlobalState;

				m_currGlobalState->Exit(m_owner);

				m_currGlobalState = state;

				m_currGlobalState->Enter(m_owner);
			}
		}
	}

	void RevertCurrentState()
	{
		assert(m_prevState && "<StateMachine::RevertCurrentState>::No previous state");

		if (m_prevState)
		{
			ChangeState(m_prevState);
		}
	}

	void RevertGlobalState()
	{
		assert(m_prevGlobalState && "<StateMachine::RevertGlobalState>::No previous global state");

		if (m_prevGlobalState)
		{
			ChangeState(m_prevGlobalState);
		}
	}

	bool IsInCurrentState(const State<entity_type>& state) const
	{
		return typeid(*m_currState) == typeid(state);
	}

	bool IsInGlobalState(const State<entity_type>& state) const
	{
		return typeid(*m_currGlobalState) == typeid(state);
	}

	State<entity_type>* GetCurrentState() { return m_currState; }
	State<entity_type>* GetGlobalState() { return m_currGlobalState; }

	const char* GetNameOfCurrentState() const
	{
		return GetNameOfState(m_currState);
	}

	const char* GetNameOfGlobalState() const
	{
		return GetNameOfState(m_currGlobalState);
	}

	static char* GetNameOfState(State<entity_type>& state)
	{
		static std::string type = typeid(state);

		if (type.size() > 5)
		{
			type.erase(0, 6);
		}

		return type;
	}
};

#endif